package com.geline.mybatisplus.interceptor.origin;

import cn.hutool.core.util.HexUtil;
import cn.hutool.core.util.ReflectUtil;
import cn.hutool.core.util.StrUtil;
import com.geline.cloud.util.StringUtils;
import com.geline.mybatisplus.annotation.DSLocked;
import com.geline.mybatisplus.handler.DSLockedHandler;
import com.geline.mybatisplus.interceptor.inner.SM4FieldHandler;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.executor.resultset.ResultSetHandler;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Signature;
import org.springframework.cglib.beans.BeanMap;

import java.lang.reflect.Field;
import java.sql.Statement;
import java.util.List;
import java.util.Objects;

/**
 * mybatis字符串属性值解密拦截器: @DSLocked
 * 使用方式：sqlSessionFactory.getConfiguration().addInterceptor(new DSLockedMybatisInterceptor());
 * @author: mx
 * @date: 2024-12-13 11:18
 */
@Intercepts({
        @Signature(type = ResultSetHandler.class, method = "handleResultSets", args = {Statement.class})
})
@Slf4j
public class DSLockedMybatisInterceptor implements Interceptor {

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        if (invocation.getTarget() instanceof ResultSetHandler) {
            // method = "handleResultSets"
            Object result = invocation.proceed();
            if (Objects.isNull(result)) {
                return null;
            }
            if (result instanceof List) {
                //基于selectList
                List<?> resultList = (List<?>) result;
                if (!resultList.isEmpty()) {
                    for (Object obj : resultList) {
                        toDecryptEntity(obj);
                    }
                }
            } else {
                //基于selectOne
                toDecryptEntity(result);
            }
            return result;
        }
        return invocation.proceed();
    }

    //解密属性
    protected void toDecryptEntity(Object entity){
        if(entity == null){
            return;
        }
        Field[] fields = ReflectUtil.getFields(entity.getClass());
        for(Field field : fields){
            DSLocked annotation = field.getAnnotation(DSLocked.class);
            if(annotation != null) {
                this.doDecrypt(entity, field, annotation);
            }
        }
    }

    protected void doDecrypt(Object object, Field field, DSLocked annotation) {
        BeanMap beanMap = BeanMap.create(object);
        String fieldName = field.getName();
        Object value = beanMap.get(fieldName);
        if(!StrUtil.isBlankIfStr(value) && HexUtil.isHexNumber(value.toString())){
            String type = annotation.value();
            if(StringUtils.isBlank(type)){
                type = "MD5";
            }
            if("MD5".equals(type.toUpperCase())){
                log.debug("MD5不支持解密 {} ====> {}", fieldName);
            }else if("SM4".equals(type.toUpperCase())){
                DSLockedHandler handler = new SM4FieldHandler();
                String newValue = handler.decryptStr(annotation.secretKey(), value.toString());
                beanMap.put(fieldName, newValue);
                log.info("解密 {} ====> {}", fieldName, newValue);
            }else{
                throw new RuntimeException("不支持的加解密类型:"+type);
            }
        }
    }
}
